﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace HQS.TokenInterpolation.Test
{
    [TestClass]
    public class TestDifferentTokens
    {
        [TestMethod]
        public void TestMathAggregateMethods()
        {
            const string template = @"
            SUM: {sum format:C this:{PriceList} expression:{PriceList.Current} * 1.06}
            AVG: {avg format:C this:{PriceList} expression:{PriceList.Current} * 1.06}
            MIN: {min format:C this:{PriceList} expression:{PriceList.Current} * 1.06}
            MAX: {max format:C this:{PriceList} expression:{PriceList.Current} * 1.06}";
            var list = new List<double>
            {
                100,
                22,
                33
            };

            var context = new DataSourcesVisitor(new {PriceList = list});
            Result result = Parser.Parse(template, context);

            double sum = list.Sum(f => f*1.06);
            double avg = list.Average(f => f*1.06);
            double min = list.Min(f => f*1.06);
            double max = list.Max(f => f*1.06);

            string parsed = template.Replace("{sum format:C this:{PriceList} expression:{PriceList.Current} * 1.06}",
                sum.ToString("C"))
                .Replace("{avg format:C this:{PriceList} expression:{PriceList.Current} * 1.06}", avg.ToString("C"))
                .Replace("{min format:C this:{PriceList} expression:{PriceList.Current} * 1.06}", min.ToString("C"))
                .Replace("{max format:C this:{PriceList} expression:{PriceList.Current} * 1.06}", max.ToString("C"));

            Assert.AreEqual(parsed, result.Output);
        }

        [TestMethod]
        public void TestSubstring()
        {
            const string template = "{substring this:{Name} from:0 length:3}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Name = "John Smith"}));
            Assert.AreEqual(result.Output, "Joh");
        }

        [TestMethod]
        public void TestPadRight()
        {
            const string template = "{padright this:{Name} with:* length:15}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Name = "John"}));
            Assert.AreEqual(result.Output, "John***********");
        }

        [TestMethod]
        public void TestPadLeft()
        {
            const string template = "{padleft this:{Name} length:15}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Name = "Bassam"}));
            Assert.AreEqual(result.Output, "         Bassam");
        }

        [TestMethod]
        public void TestLeft()
        {
            const string template = "{left this:{Name} length:3}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Name = "Bassam"}));
            Assert.AreEqual(result.Output, "Bas");
        }

        [TestMethod]
        public void TestRight()
        {
            const string template = "{right this:{Name} length:3}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Name = "Bassam"}));
            Assert.AreEqual(result.Output, "sam");
        }

        [TestMethod]
        public void TestReplaceCaseSensitive()
        {
            const string template = "{replace this:{Name} with:bas that:Wis}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Name = "Bassam"}));
            Assert.AreEqual(result.Output, "Bassam");
        }

        [TestMethod]
        public void TestReplaceCaseInSensitive()
        {
            const string template = "{replace this:{Name} with:bas that:Wis ignorecase:true}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Name = "Bassam"}));
            Assert.AreEqual(result.Output, "Wissam");
        }

        [TestMethod]
        public void TestCompute()
        {
            int price = 19;
            const string template = "{compute expression:{price} * 1.06 format:c}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {price}));
            Assert.AreEqual(result.Output, (price*1.06).ToString("C"));
        }

        [TestMethod]
        public void TestCoalesce_With()
        {
            const string template = "{coalesce this:{Name} with:{FirstName} with1:{LastName}}";
            Result result = Parser.Parse(template,
                new DataSourcesVisitor(new {Name = (string) null, FirstName = (string) null, LastName = "Test"}));

            Assert.AreEqual(result.Output, "Test");
        }

        [TestMethod]
        public void TestCoalesce_WithN()
        {
            const string template = "{coalesce this:{Name} with:{FirstName}}";
            Result result = Parser.Parse(template,
                new DataSourcesVisitor(new {Name = (string) null, FirstName = (string) null, LastName = "Test"}));

            Assert.AreEqual(result.Output, "");
        }

        [TestMethod]
        public void TestJoin()
        {
            const string template = "{join this:{Names} using:{coalesce this:{Names.Current} with:***EMPTY***} by:#}";
            Result result = Parser.Parse(template,
                new DataSourcesVisitor(new {Names = new List<string> {"Bassam", null, "John"}}));

            Assert.AreEqual(result.Output, "Bassam#***EMPTY***#John");
        }

        [TestMethod]
        public void TestIf()
        {
            var context = new
            {
                Enabled = false,
                DisabledBranch = "Evaluated as Disabled",
                EnabledBranch = "Evaluated as Enabled"
            };
            const string template = "Expression {if expression:{Enabled} then:{EnabledBranch} else:{DisabledBranch}}";
            Result result = Parser.Parse(template, new DataSourcesVisitor(context));

            Assert.AreEqual(result.Output,
                "Expression " + (context.Enabled ? context.EnabledBranch : context.DisabledBranch));
        }

        [TestMethod]
        public void TestSplit()
        {
            var context = new
            {
                Cars = "Ford,General Motors,Chrysler",
            };
            const string template = @"
            Top USA car companies 

            {foreach this:{split this:{Cars} by:,} template:car}
            @template(name:car)
            {
                {car.Position}: The string {car.Current} has {car.Current.Length} characters.
            }
            ";
            Result result = Parser.Parse(template, new DataSourcesVisitor(context));
            string output = result.Output;
        }

        [TestMethod]
        public void TestRepeat()
        {
            const string template = @"
            {foreach this:{repeat this:Message times:5} template:msg}
            @template(name:msg)
            {
                {msg.Position} --- {msg.Current}.
            }
            ";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Message = "YES"}));
            string output = result.Output;
        }

        [TestMethod]
        public void TestFloor()
        {
            double number = 12.35;
            const string template = @"The floor for {number} is {floor this:{number}}.";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {number}));
            Assert.AreEqual(result.Output,
                template.Replace("{floor this:{number}}", Math.Floor(number).ToString())
                    .Replace("{number}", number.ToString()));
        }

        [TestMethod]
        public void TestCeiling()
        {
            const double number = 12.35;
            const string template = @"The ceiling for {number} is {ceiling this:{number}}.";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {number}));
            Assert.AreEqual(result.Output,
                template.Replace("{ceiling this:{number}}", Math.Ceiling(number).ToString())
                    .Replace("{number}", number.ToString()));
        }

        [TestMethod]
        public void TestHelloWorld()
        {
            const string greeting = "Hello World";
            const string template = "Did you know that {Greeting} consists of {Greeting.Length} characters?";
            Result result = Parser.Parse(template, new DataSourcesVisitor(new {Greeting = greeting}));

            Assert.AreEqual(result.Output,
                template.Replace("{Greeting}", greeting).Replace("{Greeting.Length}", greeting.Length.ToString()));
        }

        [TestMethod]
        public void TestBegineer()
        {
            const string template = @"

            {Date format:F}
            {Company.Name}
            {Company.Address.Street}
            {Company.Address.City}, {Company.Address.State} {Company.Address.Zip}

            Dear {FirstName}: 
            
            For your record, Your PIN {right this:{PIN} length:4} has been verified.
        
            Please make sure your to reference this in any future correspondonce. 
        
            Sincerely,
            {IssuedBy.Name}, {IssuedBy.Title}
            ";
            var root = new
            {
                PIN = 123456789,
                FirstName = "John",
                LastName = "Smith",
                Date = new DateTime(2014, 8, 31, 4, 2, 2),
                IssuedBy = new
                {
                    Name = "Joe Smith",
                    Title = "Manager"
                },
            };

            var context = new DataSourcesVisitor(root);
            context["Company"] = new
            {
                Name = "Company Name, Inc",
                Address = new {Street = "900  US HWY 1", City = "Boynton Beach", State = "FL", Zip = "33436"}
            };

            string result = Parser.Parse(template, context).Output;
        }

        [TestMethod]
        public void TestSimpleUsage()
        {
            const string template =
                @"
            {Today format:F}
            {Company.Name}
            {Company.Address.Street}
            {Company.Address.City}, {Company.Address.State} {Company.Address.Zip}

            Dear {Customer.FirstName} {Customer.LastName}: 
    
            Thanks for registering to become a member of our spam list. 

            Please note your user id is your email address {Customer.Email} and your password 
            is your card that ends with {padleft length:{Customer.CardNumber.Length} with:* this:{right this:{Customer.CardNumber} length:4}}

            If you have any questions, please feel free to contact me by email {Manager.Email} or by phone {Manager.PhoneNumber format:(###) ###-####}

            Thanks
            {Manager.FullName}
            ";

            var rootData = new
            {
                DateTime.Today,
                Manager = new
                {
                    FullName = "Joe Clinton",
                    PhoneNumber = 8883438899,
                    Email = "Joe@HQS.com"
                },
                Company = new
                {
                    Name = "HQS, Inc",
                    Address = new {Street = "900  US HWY 1", City = "Boynton Beach", State = "FL", Zip = "33436"}
                },
                Customer = new
                {
                    Email = "test@test.com",
                    FirstName = "John",
                    LastName = "Smith",
                    CardNumber = "12345678912345"
                },
            };


            string result = Parser.Parse(template, new DataSourcesVisitor(rootData)).Output;
        }

        [TestMethod]
        public void TestForEach()
        {
            const string template = @"{foreach this:{Orders} template:order}
            @template(name:order)
            {
               Number: {order.Current.Number} Price: {order.Current.Price}
            }";
            Result result = Parser.Parse(template,
                new DataSourcesVisitor(new {Orders = new List<object> {new {Number = 1, Price = 300}}}));
            string res = result.Output;
        }
    }
}